#pragma once
#include <functional>
#include "acl_cpp/lib_acl.hpp"
namespace acl
{
namespace mqtt
{
class socket_session :public aio_callback
{
public:
	enum 
	{
		arbitrary = 0
	};
	socket_session ()
	{

	}
	~socket_session ()
	{
		destory();
	}
	socket_session (socket_session &&_socket_session)
	{
		_socket_session.detach_aio_socket_stream ();
		bind_aio_socket_stream(_socket_session.socket_stream_);
		_socket_session.socket_stream_ = nullptr;
	};
	socket_session & operator= (socket_session &&_socket_session)
	{
		destory();
		_socket_session.detach_aio_socket_stream ();
		bind_aio_socket_stream(_socket_session.socket_stream_);
		_socket_session.socket_stream_ = nullptr;
		return *this;
	}
	socket_session &
		bind_aio_socket_stream (aio_socket_stream* _socket_stream)
	{
		destory();
		_socket_stream->add_read_callback (this);
		_socket_stream->add_close_callback (this);
		_socket_stream->add_timeout_callback (this);
		_socket_stream->add_write_callback (this);
		socket_stream_ = _socket_stream;
		return *this;
	}
	template<class RecvCallBack>
	socket_session &bind_recv_callback (RecvCallBack callback)
	{
		recv_callback_ = callback;
		return *this;
	}
			
	template<class CallBack>
	socket_session &bind_send_callback (CallBack callback)
	{
		send_callback_ = callback;
		return *this;
	}
	template<class CallBack>
	socket_session &bind_timeout_callback(CallBack callback) 
	{
		timeout_callback_ = callback;
		return *this;
	}
	template<class CallBack>
	socket_session &bind_disconnect_callback(CallBack callback) 
	{
		close_callback_ = callback;
		return *this;
	}
	void send (std::string &&buffer)
	{
		socket_stream_->write (buffer.data (), buffer.size ());
	}
	void send (const void *data, int len)
	{
		socket_stream_->write (data, len);
	}
	void to_recv (int size = arbitrary, int timeout_seconds = 0)
	{
		socket_stream_->read (size, timeout_seconds);
	}
	std::string get_ip()
	{
		return socket_stream_->get_peer();
	}
	void destory()
	{
		if(!socket_stream_)
			return;
		detach_aio_socket_stream();
		socket_stream_->close();
		socket_stream_ = nullptr;
	}
	void close()
	{
		if(!socket_stream_)
			return;
		socket_stream_->close();
	}
private:
	socket_session (const socket_session&)
	{};
	const socket_session& operator=(const socket_session&)
	{ return *this; };
	void detach_aio_socket_stream ()
	{
		if(!socket_stream_)
			return;
		socket_stream_->del_close_callback (this);
		socket_stream_->del_read_callback(this);
		socket_stream_->del_write_callback(this);
		socket_stream_->del_timeout_callback(this);
	}
	virtual void close_callback () override
	{
		socket_stream_ = nullptr;
		if(close_callback_)
			close_callback_ (*this);
	}

	virtual bool read_callback (char* data, int len) override
	{
		assert (recv_callback_);
		return recv_callback_ (data, len);
	}
	virtual bool timeout_callback () override
	{
		if(timeout_callback_)
			return timeout_callback_ (*this);
		return true;
	}
	virtual bool write_callback () override
	{
		if (send_callback_)
			send_callback_ (*this);
		return true;
	}
private:
	aio_socket_stream *socket_stream_ = nullptr;
	std::function<bool (char *, int)> recv_callback_;
	std::function<bool(socket_session &)> send_callback_;
	std::function<bool (socket_session &)> close_callback_;
	std::function<bool (socket_session &)> timeout_callback_;
	std::function<bool(socket_session &)> recv_timeout_callback_;
};
}
}
